package com.project.springboot.utils;

import com.gitee.yongzhuzl.commonutil.util.math.MathUtil;

import java.math.BigDecimal;
import java.util.*;

/**
 * description: 数字相关工具类
 * @author 朱正磊
 * @date 2024-04-28 08:32:15
 */
public class BigDecimalUtil {

    /**
     * description: 获取集合中元素以及对应的出现次数
     * @author 朱正磊
     * @date 2024-04-28 08:44:50
     * @param list 数字集合
     * @return 返回集合中元素以及对应的出现次数组成的集合
     */
    public static List<String> listEleAndCount(List<BigDecimal> list) {
        Set<BigDecimal> set = new HashSet<>(list);
        List<String> strList = new ArrayList<>();
        for (BigDecimal num : set) {
            // 获取指定集合中指定元素出现的频次
            int frequency = Collections.frequency(list, num);
            strList.add(num + "-" + frequency);
        }
        return strList;
    }

    /**
     * description: 在所有可能的拆分组合中根据可以拆分成的数的最大个数限制获取最终的拆分组合
     * @author 朱正磊
     * @date 2024-04-26 14:01:14
     * @param listList 所有可能的拆分组合
     * @param specs 可以拆分成的数的集合
     * @param nums 可以拆分成的数的最大个数的集合
     * @return 返回最终的拆分组合
     */
    public static List<List<BigDecimal>> getNeedList(List<List<BigDecimal>> listList, List<BigDecimal> specs, List<Integer> nums) {
        for (int j = listList.size() - 1; j > -1; j--) {
            List<BigDecimal> list = listList.get(j);
            boolean flag = false;
            for (int i = 0; i < specs.size(); i++) {
                // 获取指定集合中指定元素出现的频次
                int frequency = Collections.frequency(list, specs.get(i));
                if (frequency > nums.get(i)) {
                    flag = true;
                    break;
                }
            }
            if (flag) {
                listList.remove(list);
            }
        }
        return listList;
    }

    /**
     * description: 判断数字a能否恰好拆分成一组数字b, c, ... n
     * @author 朱正磊
     * @date 2024-04-20 10:42:41
     * @param a 待拆分的数
     * @param nums 可以拆分成的数的集合
     * @return 如果a能被nums恰好拆分返回true，否则返回false
     */
    public static boolean canSplit(BigDecimal a, List<BigDecimal> nums) {
        // 如果没有给定数字或a负数，则无法拆分
        if (nums.size() == 0 || MathUtil.compare(a, new BigDecimal(0)) == -1) {
            return false;
        }

        // 使用递归搜索所有可能的拆分组合
        return canSplitHelper(a, nums, 0);
    }

    /**
     * description: 递归辅助函数，在给定的一组数字中搜索拆分组合
     * @author 朱正磊
     * @date 2024-04-20 10:43:00
     * @param a 待拆分的数
     * @param nums 可以拆分成的数的集合
     * @param index 索引，从0开始
     * @return 如果a能被nums恰好拆分返回true，否则返回false
     */
    public static boolean canSplitHelper(BigDecimal a, List<BigDecimal> nums, Integer index) {
        // 如果a为0，则找到一种拆分方案，返回true
        if (MathUtil.compare(a, new BigDecimal(0)) == 0) {
            return true;
        }

        // 递归搜索所有可能的拆分组合
        for (int i = index; i < nums.size(); i++) {
            // 如果当前数字nums[i]小于等于a，则尝试将a拆分成nums[i]和剩余部分
            if (MathUtil.compare(nums.get(i), a) == 0 || MathUtil.compare(nums.get(i), a) == -1) {
                if (canSplitHelper(new BigDecimal(MathUtil.subHalfUp(a.toString(), nums.get(i).toString())), nums, i)) {
                    return true;
                }
            }
        }

        // 无法找到拆分方案
        return false;
    }

    /**
     * description: 获取所有可能的拆分组合
     * @author 朱正磊
     * @date 2024-04-20 10:43:12
     * @param a 待拆分的数
     * @param nums 可以拆分成的数的集合
     * @return 返回所有可能的拆分组合
     */
    public static List<List<BigDecimal>> getAllSplits(BigDecimal a, List<BigDecimal> nums) {
        List<List<BigDecimal>> result = new ArrayList<>();
        if (nums.size() == 0 || MathUtil.compare(a, new BigDecimal(0)) == -1) {
            return result;
        }

        List<BigDecimal> current = new ArrayList<>();
        getAllSplitsHelper(a, nums, 0, current, result);
        return result;
    }

    /**
     * description: 递归辅助函数，获取所有可能的拆分组合
     * @author 朱正磊
     * @date 2024-04-20 10:43:23
     * @param a 待拆分的数
     * @param nums 可以拆分成的数的集合
     * @param index 索引，从0开始
     * @param current 当前集合
     * @param result 拆分组合
     */
    public static void getAllSplitsHelper(BigDecimal a, List<BigDecimal> nums, Integer index, List<BigDecimal> current, List<List<BigDecimal>> result) {
        if (MathUtil.compare(a, new BigDecimal(0)) == 0) {
            result.add(new ArrayList<>(current));
            return;
        }

        for (int i = index; i < nums.size(); i++) {
            if (MathUtil.compare(nums.get(i), a) == 0 || MathUtil.compare(nums.get(i), a) == -1) {
                current.add(nums.get(i));
                getAllSplitsHelper(new BigDecimal(MathUtil.subHalfUp(a.toString(), nums.get(i).toString())), nums, i, current, result);
                current.remove(current.size() - 1);
            }
        }
    }

}
